/*
 * 		CSC 660, Program # 1
 * 		Raj Akula, Rubaiyat Hossain
 * 		03/26/2012
 * 		Description: A socket-based, multithreaded server that handles simple HTTP 1.0
 * 					 requests. 
 * 					 
 */

package nku.edu.webserver;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.StringTokenizer;

public class HttpRequest implements Runnable
{
	Socket socket=null;
	final static String CRLF="\r\n";//end of the line character sequence
	BufferedReader br;
	DataOutputStream pw;

	public HttpRequest(Socket socket) throws Exception
	{
		this.socket=socket;
	}

	@Override
	public void run()
	{
		try
		{	
			//since run method does not throw exception, we calling processRequest() to handle the request
			processRequest();
		}
		catch(Exception e)
		{
			System.out.println(e);
		}
	}

	private void processRequest() throws Exception
	{
		//initializing socket's input and output streams.
		br = openInputStream(this.socket);
		pw = openOutputStream(this.socket);
		String url = readLine(br);
		String fileName = "";

		StringTokenizer tokens = new StringTokenizer(url);
		tokens.nextToken();

		//get teh file name from the request line
		if(tokens.hasMoreTokens())
		{
			fileName = tokens.nextToken();
			fileName = "." + fileName; //Prepend a . to the file name to make sure that the file is within the current directory. 
		}

		FileInputStream fis = null;
		boolean fileExists = false;
		
		//open the requested file, if it does not exist then show error
		try
		{
			fis = new FileInputStream(fileName);
			fileExists = true;
		}
		catch(Exception E)
		{
			System.out.println("This file asked doesn't exist: " + fileName);
		}

		//Process the statusLine,ContentTypeLine, entityBody for response
		String statusLine = "";
		String contentTypeLine = "";
		String entityBody = "";
		if(fileExists)
		{
			statusLine = "HTTP/1.0 200 OK" + CRLF;
			contentTypeLine = "Content-type: " + contentType( fileName ) + CRLF;
		}
		else
		{
			statusLine = "HTTP/1.0 404 Not Found" + CRLF;
			contentTypeLine = "Content-Type: text/html" + CRLF;
			entityBody = "<HTML>" + "<HEAD><TITLE>Not Found</TITLE></HEAD>" + "<BODY>Not Found</BODY></HTML>";
		}

		this.sendMessage(pw, statusLine);
		this.sendMessage(pw, contentTypeLine);
		this.sendMessage(pw, CRLF);

		if(fileExists)
		{
			this.sendBytes(fis);
		}
		else
		{
			this.sendMessage(pw, entityBody);
		}
		this.closeEverything();
	}

	private void sendBytes(FileInputStream fis) throws Exception
	{
		// TODO Auto-generated method stub
		DataOutputStream os = new DataOutputStream(socket.getOutputStream());

		byte[] buffer = new byte[1024];
		int bytes = 0;

		while((bytes = fis.read(buffer)) != -1)
		{
			os.write(buffer, 0, bytes);
		}
		os.flush();
		os.close();

	}

	public String contentType(String fileName)
	{
		String fileType = "";

		if(fileName.endsWith(".htm") || fileName.endsWith(".html"))
		{
			fileType = "text/html";
		}
		else if(fileName.endsWith(".jpg") || fileName.endsWith(".jpeg"))
		{
			fileType = "text/jpeg";
		}
		else if(fileName.endsWith(".gif"))
		{
			fileType = "text/gif";
		}
		else if(fileName.endsWith(".txt"))
		{
			fileType = "text/plain";
		}
		else if(fileName.endsWith(".pdf"))
		{
			fileType = "application/pdf";
		}
		else
		{
			fileType = "application/octet-stream";
		}
		return fileType;
	}

	public void closeEverything() throws Exception
	{
		br.close();
		pw.close();
		socket.close();
	}

    //Open an input stream with a specific socket
    public BufferedReader openInputStream(Socket socket)
    {
    	try{
			DataInputStream inputStream = new DataInputStream(socket.getInputStream());
			BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));
			System.out.println("The input stream is opened to recieve messages from client!");
			return input;
		}
		catch(IOException e){
			System.out.println(e);
			System.out.println("Failed to open an input stream to recieve messages from client!");
			System.exit(1);
		}
		return null;
    }
    

    //Open an output stream with a specific socket
    public DataOutputStream openOutputStream(Socket socket)
    {
		try
		{
			DataOutputStream output = new DataOutputStream(socket.getOutputStream());
			System.out.println("The output stream is opened to send messages to client!");
			return output;
		}
		catch(IOException e){
			System.out.println(e);
			System.out.println("Failed to open stream to send messages to client!");
			System.exit(1);
		}
		return null;
    }

    //sendmessages at a give print writer
    public void sendMessage(DataOutputStream output, String... lines) throws Exception
    {
    	for(String line: lines)
    	{
    		output.writeBytes(line);
    	}
    	output.flush();
    }


    //readline specified buffered reader
    public String readLine(BufferedReader input)
    {
    	String line = null;
    	try
    	{
    		line = input.readLine();
		}
		catch(IOException e)
		{
			System.out.println(e);
			System.out.println("Communication Failed!");
			System.exit(1);
		}
		return line;
    }
}
